package com.lin.benchmade.utils;

import java.io.IOException;
import java.io.Reader;

import org.apache.lucene.analysis.Tokenizer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
import org.apache.lucene.util.AttributeSource;

/**
 * 按文本类型进行划分
 * @author xiaolin
 */
public final class TypeTokenizer extends Tokenizer {

	public TypeTokenizer(Reader in) {
		super(in);
	}

	public TypeTokenizer(AttributeSource source, Reader in) {
		super(source, in);
	}

	public TypeTokenizer(AttributeFactory factory, Reader in) {
		super(factory, in);
	}

	private int bufferIndex = 0, dataLen = 0;
	private final static int MAX_WORD_LEN = 100;
	private final static int IO_BUFFER_SIZE = 2;
	private final char[] buffer = new char[MAX_WORD_LEN];
	private final char[] ioBuffer = new char[IO_BUFFER_SIZE];
	private int posIncr = 1;

	private int length;

	private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
	private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
	private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class);

	private final void push(char c) {
		buffer[length++] = Character.toLowerCase(c); // buffer it
	}

	private final boolean flush(int type) {
		if (length > 0) {
			termAtt.copyBuffer(buffer, 0, length);
			// offsetAtt.setOffset(correctOffset(start), correctOffset(start +
			// length));
			posIncrAtt.setPositionIncrement(posIncr++);// 每返回一次，postion就递增一个
			typeAtt.setType("" + type);
			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean incrementToken() throws IOException {
		clearAttributes();
		length = 0;
		int lastType = -1;
		while (true) {
			final char c;
			if (bufferIndex >= dataLen) {
				dataLen = input.read(ioBuffer);
				bufferIndex = 0;
			}
			if (dataLen == -1) {
				return flush(lastType);
			} else {
				c = ioBuffer[bufferIndex++];
			}

			if (length == MAX_WORD_LEN) {
				bufferIndex--;
				return flush(lastType);
			}

			switch (Character.getType(c)) {
			case Character.LOWERCASE_LETTER: {
				if (lastType == Character.LOWERCASE_LETTER || lastType == Character.UPPERCASE_LETTER || lastType == -1) {
					push(c);
					lastType = Character.LOWERCASE_LETTER;
				} else {
					bufferIndex--;
					return flush(lastType);
				}
				break;
			}
			case Character.DECIMAL_DIGIT_NUMBER:
			case Character.UPPERCASE_LETTER: {
				if (lastType == Character.getType(c) || lastType == -1) {
					push(c);
					lastType = Character.getType(c);
				} else {
					bufferIndex--;
					return flush(lastType);
				}
				break;
			}
			case Character.OTHER_LETTER:
				if (length > 0) {
					bufferIndex--;
					return flush(lastType);
				}
				push(c);
				lastType = Character.OTHER_LETTER;
				return flush(lastType);
			default:
				if (length > 0)
					return flush(lastType);
				break;
			}
		}
	}

	@Override
	public void reset() throws IOException {
		super.reset();
		posIncr = 1;
	}

	@Override
	public void reset(Reader input) {
		try {
			super.reset(input);
			reset();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
